import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashSet;
import java.util.Scanner;
import java.util.concurrent.ConcurrentLinkedQueue;

//String.split on a regular expression <.*> as regular expression
//afs
public class Parser extends Thread
{
	private ConcurrentLinkedQueue<Page> pageBuffer;
	private ConcurrentLinkedQueue<URL> pageToRetrieveQueue;
	private DataGatherer gatherer;
	private Stopper finished;
	private final String HREF = "a href=\"";
	private String dir;
	private HashSet<String> prevPages;
	private ConcurrentLinkedQueue<PageData> pageDataQueue;
	private int urlCount;

	public Parser( ConcurrentLinkedQueue<Page> pageBuffer, ConcurrentLinkedQueue<URL> pageToRetrieveQueue,
				   DataGatherer gatherer, Stopper finished, ConcurrentLinkedQueue<PageData> pageDataQueue)
	{
		this.pageBuffer = pageBuffer;
		this.pageToRetrieveQueue = pageToRetrieveQueue;
		this.gatherer = gatherer;
		this.finished = finished;
		this.pageDataQueue = pageDataQueue;
		urlCount = 0;
		dir = "";
		prevPages = new HashSet<String>();
	}
	
	public void parse( String page )
	{
		Scanner scan = new Scanner(page);
		
		while ( scan.hasNextLine() )
		{		
			String s = scan.nextLine();
			
			String builtURL = buildUrl( s );
			
			if ( isValidURL( builtURL ) )
			{
				URL url = null;
				
				try
				{
					url = new URL(builtURL);
					pageToRetrieveQueue.add( url );
					
					urlCount++;
				}
				catch (MalformedURLException e)
				{
					if ( url != null )
					{
						System.out.println("Bad url: " + url.toString());
					}
					else
					{
						System.out.println( "null url" );
					}
				}
			}				
		}
	}
	
	public boolean isValidURL( String url )
	{
		boolean valid = false;
		
		if (url != "" && url.matches( "http://.*\\.(html|txt|com|gov|edu|org|info|asp|shtml)(/.*/|/|#.*|)" ))
		{
			if ( !prevPages.contains( url ))
			{
				valid = true;
				prevPages.add( url );
			}
		}

		return valid;
	}
	
	private String buildUrl( String url )
	{
		String builtURL = "";
		
		while ( isValidStartIndex( url ) )
		{
			int startIndex = url.indexOf( HREF ) + HREF.length();
			
			if  ( isValidEndIndex( url, startIndex ) )
			{
				int endIndex = url.indexOf( "\">", startIndex + HREF.length() );
				
				// Get the link
				String link = url.substring( startIndex, endIndex );
				
				// fix relative links
				if ( isRelative(link) )
				{
					builtURL = makeExplicit( link );
				}
				else if ( link.contains( "http://" ) )
				{
					builtURL = link;
				}
				
				// url = everything after the found link.
				url = url.substring( endIndex + 1 ); 
			}
			// No end bracket
			else
			{
				url = "";
			}
		}
		
		return builtURL;
	}
	
	private boolean isValidStartIndex( String url )
	{
		boolean validStart = false;
		
		if ( url.indexOf( HREF ) != -1 )
		{
			validStart = true;
		}
		
		return validStart;
	}
	
	private boolean isValidEndIndex( String url, int startIndex )
	{
		boolean validEnd = false;
		
		if ( url.indexOf( "\">", startIndex + HREF.length() ) != -1 )
		{
			validEnd = true;
		}
		
		return validEnd;
	}
	
	private boolean isRelative( String link )
	{
		boolean isRelative = false;
		
		if ( !link.contains( "http://" ) && (link.contains( ".html" ) || link.contains( ".txt" )))
		{
			isRelative = true;
		}
		
		return isRelative;
	}
	
	private String makeExplicit( String link )
	{
		// Get rid of ..
		while  ( link.charAt( 0 ) == '.' )
		{
			link = link.substring( 3 );
		}
		
		//Add a / to the front if one does not exist
		if ( !(link.charAt( 0 ) == '/') )
		{
			link = '/' + link;
		}

		return dir + link;
	}
	
	public void setDir( String dir )
	{
		// get domain/path
		if ( dir.contains( ".html" ) || dir.contains( ".txt" ) )
		{
			this.dir = dir.substring( 0, dir.lastIndexOf("/", dir.lastIndexOf( "." ) ));
		}
		// delete ending / if there
		else if ( dir.charAt( dir.length() - 1 ) == '/' )
		{
			this.dir = dir.substring( 0, dir.length() - 1 );
		}
		else
		{
			this.dir = dir;
		}
	}
	
	public int getURLCount()
	{
	  int i = urlCount;
	  urlCount = 0;
	  return i;
	}
	
	public void run()
	{
		while( gatherer.getPagesRetrieved() < DataGatherer.PAGE_LIMIT )
		{
			if ( !pageBuffer.isEmpty() )
			{
				long startParseTime = System.currentTimeMillis();
				
				//System.out.println("parsing");
				finished.setParserWaiting( false );
				
				Page curPage = pageBuffer.poll();
				
				setDir( curPage.getURL() );
				parse( curPage.getContent() );
				
				pageDataQueue.add( new PageData( curPage.getContent(), System.currentTimeMillis() - startParseTime,
								    urlCount) );
				urlCount = 0;
			}
			else
			{
				if ( pageToRetrieveQueue.isEmpty() )
				{
					finished.setParserWaiting( true );
					//System.out.println("parser wait");
					
					// Nothing left to do
					if ( finished.getParserWaiting() && finished.getRetrieverWaiting() )
					{
						break;
					}
				}
				//System.out.println("parser waiting");
				try
				{
					sleep(10);
				}
				catch( InterruptedException e)
				{
					
				}
			}
		}
	}
}
